package com.beta.service.pay;

import com.beta.dto.pay.yeepay.YeepayNotifyDto;
import com.beta.em.order.OrderStatus;
import com.beta.em.order.PayStatus;
import com.beta.em.order.TransactionType;
import com.beta.em.pay.PayRoutes;
import com.beta.em.pay.YeepayCardType;
import com.beta.em.pay.YeepayNotifyType;
import com.beta.em.pay.YeepayPayProduct;
import com.beta.entity.OrderSettleQueues;
import com.beta.entity.Orders;
import com.beta.entity.Payments;
import com.beta.event.order.OrderEvent;
import com.beta.event.order.OrderPayloadEvent;
import com.beta.pay.yeepay.core.Yeepay;
import com.beta.service.*;
import com.beta.service.impl.PublishService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created by vector on 2016/12/6.
 */
@Service
public class YeepayService implements IYeepayService {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private IPaymentsService paymentsService;
    @Autowired
    private IOrdersService ordersService;
    @Autowired
    private IOrderStatusService orderStatusService;
    @Autowired
    private IOrderOperateService orderOperateService;
    @Autowired
    private ITransactionsService transactionsService;
    @Autowired
    private IDateTimeService dateTimeService;
    @Autowired
    private PublishService publishService;
    @Autowired
    private IOrderSettleQueuesService settledQueuesService;
    @Autowired
    private Yeepay yeepay;

    @Override
    public YeepayNotifyDto decodeNotifyData(String notifyData) {
        Map<String, String> dataMap = yeepay.pays().decodeApiResponse(notifyData);
        return buildPayNotifyInfo(dataMap);
    }

    @Override
    public YeepayNotifyDto executeHandlePayNotifyData(String notifyData) {
        YeepayNotifyDto notifyDto = decodeNotifyData(notifyData);
        handlePaySuccessNotifyData(notifyDto);
        return notifyDto;
    }

    @Override
    public void executeHandleDivideNotify(String data) {
        Map<String, String> dataMap = yeepay.divide().decodeApiResponse(data);
        String paymentCode = MapUtils.getString(dataMap, "orderrequestid");
        Payments payments = paymentsService.selectOne(new Payments().setCode(paymentCode));
        if (Objects.isNull(payments)) {
            handleDivideNotifyError(String.format("非法的订单 ==> paymentCode=%s", paymentCode));
        }

        // 处理分账通知
        Orders orders = ordersService.selectById(payments.getOrderId());
        if (orderStatusService.isSettled(orders)) {
            // 已经结算过，忽略，防止易宝重复回调通知
            logger.info("分账结算回调通知 ==> 订单已结算,忽略通知");
            return ;
        }
        handleDivideNotify(orders, dataMap);
    }

    private void handleDivideNotify(Orders order, Map<String, String> dataMap) {
        List<OrderSettleQueues> orderSettleQueues = settledQueuesService.getOrderSettleQueues(order.getId(), TransactionType.SETTLED);
        if (CollectionUtils.isEmpty(orderSettleQueues)) {
            return ;
        }

        String balanceCode = dataMap.get("requestid");
        orderSettleQueues.forEach(settleQuere -> {
            transactionsService.createSettleTransaction(balanceCode, settleQuere, order);
        });

        // 修改状态
        orderStatusService.changeStatus(order.getId(), OrderStatus.SETTLED, null);

        // 发布订单分账成功事件
        Orders orders = ordersService.selectById(order.getId());
        publishService.publishApplicationEvent(new OrderPayloadEvent(OrderEvent.SETTLED, orders));
    }

    private void handlePaySuccessNotifyData(YeepayNotifyDto notifyDto) {
        Assert.notNull(notifyDto);
        if (YeepayNotifyType.REDIRECT.equals(notifyDto.getNotifyType())) {
            // ignore
            return ;
        }

        // 1. payment[status] ==> paid
        // 2. createPayTransaction
        // 3. update order status
        String paymentCode = notifyDto.getRequestId();
        Payments payment = updatePayment(paymentCode, notifyDto);
        if (Objects.isNull(payment)) {
            handlePayNotifyError(String.format("非法的支付 ==> code=%s", paymentCode));
        }
        Orders order = getOrder(payment.getOrderId());
        if (Objects.isNull(order)) {
            handlePayNotifyError(String.format("未找到关联的订单 ==> orderId=%s", payment.getOrderId()));
        }
        // 创建交易单
        transactionsService.createPayTransaction(notifyDto.getExternalId(), notifyDto.getCardNumber(), order, payment);
        // 订单支付确认
        orderOperateService.executePaymentConfirm(order.getId());
    }

    private Payments updatePayment(String paymentCode, YeepayNotifyDto notifyDto) {
        Payments whereEntity = new Payments().setCode(paymentCode);
        Payments updateEntity = new Payments()
                .setUpdatedAt(dateTimeService.toDbDate())
                .setStatus(PayStatus.PAID.getValue())
                .setPayRoutes(getPayRoutes(notifyDto.getPayProduct()).value());
        paymentsService.updateSelective(updateEntity, whereEntity);
        return paymentsService.selectOne(whereEntity);
    }

    private PayRoutes getPayRoutes(YeepayPayProduct payProductType) {
        switch (payProductType) {
            case WECHATU:
            case WECHATG:
            case WECHAT_APP:
            case WAP_WECHATAPP:
                return PayRoutes.WEPAY;
            case ALIPAY_APP:
            case WAP_ALIPAYAPP:
                return PayRoutes.ALIPAY;
            default:
                return PayRoutes.YEEPAY;
        }
    }

    private Orders getOrder(Integer orderId) {
        Assert.notNull(orderId);
        return ordersService.selectById(orderId);
    }

    private YeepayNotifyDto buildPayNotifyInfo(Map<String, String> dataMap) {
        YeepayNotifyDto notifyInfo  = new YeepayNotifyDto();
        notifyInfo.setRequestId(MapUtils.getString(dataMap, "requestid"));
        notifyInfo.setCode(MapUtils.getString(dataMap, "code"));
        notifyInfo.setNotifyType(YeepayNotifyType.getNotifyType(MapUtils.getString(dataMap, "notifytype")));
        notifyInfo.setExternalId(MapUtils.getString(dataMap, "externalid"));
        notifyInfo.setAmount(MapUtils.getString(dataMap, "amount"));
        notifyInfo.setCardNumber(MapUtils.getString(dataMap, "cardno"));
        notifyInfo.setBankCode(MapUtils.getString(dataMap, "bankcode"));

        // 以下三种属性在 notifyType=server时存在
        if (dataMap.containsKey("cardtype")) {
            notifyInfo.setCardType(YeepayCardType.getCardType(MapUtils.getString(dataMap, "cardtype")));
        }
        if (dataMap.containsKey("paydate")) {
            notifyInfo.setPayDate(new Date(MapUtils.getLong(dataMap, "paydate")));
        }
        if (dataMap.containsKey("payProduct")) {
            notifyInfo.setPayProduct(YeepayPayProduct.getPayProduct(MapUtils.getString(dataMap, "payProduct")));
        }
        return notifyInfo;
    }

    private String handlePayNotifyError(String errorMessge) {
        logger.error("易宝支付 - 回调通知处理异常：{}", errorMessge);
        throw new ServiceException("E60001", errorMessge);
    }

    private String handleDivideNotifyError(String errorMessge) {
        logger.error("易宝分账 - 回调通知处理异常：{}", errorMessge);
        throw new ServiceException("E70001", errorMessge);
    }

}
